/*
 * Created by SharpDevelop.
 * User: Michael
 * Date: 18/11/2007
 * Time: 6:15 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;

namespace SudokuSolver
{
	/// <summary>
	/// Description of BlockLine.
	/// </summary>
	public class BlockLine : Technique
	{
		public override bool Run(SudokuGame theGame)
		{
			bool change = false;
			//foreach block in the game
			foreach(Group thisBlock in theGame.Blocks)
			{
				//generate the list of remaining numbers to find and their frequency among candidate lists
				Dictionary<int, int> frequencyTable = new Dictionary<int, int>();
				for(int i = 1; i <= 9; i++)
				{
					//if this block is missing i,
					if(thisBlock.MissingNumber(i))
					{
						//caluclate it's frequency
						int frequency = 0;
						foreach(Cell thisCell in thisBlock.Cells)
						{
							if(!thisCell.Found)
							{
								if(thisCell.IsPossible(i))
									frequency++;
							}
						}
						//add it to the table
						frequencyTable.Add(i, frequency);
					}
				}
				
				//trim the table so that no entry contains more than 3 or less than 2 possible cells
				List<int> toRemove = new List<int>();
				foreach(int candidate in frequencyTable.Keys)
				{
					if((frequencyTable[candidate] > 3) || (frequencyTable[candidate] < 2))
						toRemove.Add(candidate);
				}
				
				foreach(int index in toRemove)
				{
					frequencyTable.Remove(index);
				}
					
				//table is complete, but don't do any more if it is empty...
				
				if(frequencyTable.Count > 0)
				{
					foreach(int candidate in frequencyTable.Keys)
					{
						List<Cell> possibleCells = GetCellsThatCanContainX(candidate, thisBlock);
						bool inSameGroup = true;
						int targetGroupNo = possibleCells[0].ColumnNumber;
						Group targetGroup = null;
						foreach(Cell cell in possibleCells)
						{
							if(cell.ColumnNumber != targetGroupNo)
								inSameGroup = false;
						}
						//did it work for columns?
						if(inSameGroup) //in same column
						{
							targetGroup = possibleCells[0].Column;
						} else {
							//try for rows
							targetGroupNo = possibleCells[0].RowNumber;
							inSameGroup = false;
							foreach(Cell cell in possibleCells)
							{
								if(cell.ColumnNumber != targetGroupNo)
									inSameGroup = false;
							}
							//did it work for rows?
							if(inSameGroup)
							{
								targetGroup = possibleCells[0].Row;
							}
						}
						
						//are the possible cells all in the same group?
						if(targetGroup != null)
						{
							//at this point, a block is missing some numbers,
							//all of the cells which can contain one of those numbers are in the same row/column
							//now, all we need to do is eliminate that number from the other cells that
							//are in the same row/column, but not the same block.
							foreach(Cell cell in targetGroup.Cells)
							{
								if(cell.Block != thisBlock)
								{
									if(cell.IsPossible(candidate))
									{
										cell.IsPossible(candidate, false);
										//something changed!
										change = true;
									}
								}
							}
						}
					}
				}
			}
			
			return change;
		}
		
		private List<Cell> GetCellsThatCanContainX(int x, Group inBlock)
		{
			List<Cell> returnList = new List<Cell>();
			foreach(Cell cell in inBlock.Cells)
			{
				if(cell.IsPossible(x))
					returnList.Add(cell);
			}
			return returnList;
		}
	}
}
